Skip to content

mcp: preserve numbers in schemas and metadata - #1311

Draft
Tangerg wants to merge 5 commits into
modelcontextprotocol:mainfrom
Tangerg:codex/preserve-protocol-json-numbers
Draft

Tangerg wants to merge 5 commits into
modelcontextprotocol:mainfrom
Tangerg:codex/preserve-protocol-json-numbers

Conversation

@Tangerg

@Tangerg Tangerg commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Tool, ElicitParams and CallToolResult decoded the numbers in their
schemas and metadata as float64. A client therefore saw a schema
constant such as 9007199254740993 as 9007199254740992, and a constant
outside the range of float64, such as 1e400, made the whole tools/list
result fail to decode. StructuredContent already keeps exact numbers, so
a client could see a schema constraint that differs from the value the
server returned.

These types now decode with UseNumber, so their numbers are json.Number,
as in StructuredContent. The fix is in the protocol types, so it applies
to every transport, to input requests, and to direct JSON decoding. Tool
and ElicitParams gain UnmarshalJSON methods for this, which a type
embedding them inherits, as with the existing methods on CallToolResult.
They decode in place, so apart from how numbers are represented,
decoding behaves as encoding/json did before.

As requested in the review of #1239, the change of dynamic type is gated
behind an opt-out: MCPGODEBUG=schemaandmetafloat64=1 restores float64
decoding until 1.11.0.

Without the change, the new tests see rounded numbers and fail to decode
1e400.

Fixes #1310

AI assistance: prepared with Claude; I reviewed the change and can
explain it.

Tangerg and others added 2 commits November 5, 2026 17:33
ElicitParams decoded with the default float64 representation, so numeric
constraints in a requested schema and numbers in _meta above 2^53 or with
long fractions were rounded before the client saw them. Decode with
UseNumber, as tool schemas and metadata now do.

Decoding into a separate value also leaves the receiver untouched when the
input is malformed, instead of half-populated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Tangerg

Tangerg commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

Restored the branch (it was deleted by mistake) and merged current main. New commit c107509 decodes ElicitParams with UseNumber as well, so requested-schema constraints and _meta numbers survive, and a malformed payload no longer leaves the receiver half-populated. go test -race ./... passes on top of f91620f.

AI assistance: prepared with Claude; I reviewed the change and can explain it.

Decoding schemas and metadata with UseNumber changes the dynamic type of
their numbers from float64 to json.Number, a behavioral change for
existing users. As with structuredcontentfloat64,
MCPGODEBUG=schemaandmetafloat64=1 restores float64 decoding until 1.11.0.

Tool and ElicitParams decode in place, so apart from how numbers
are represented they decode exactly as encoding/json did before: fields
absent from the input keep their values, and null is a no-op.

The tests are consolidated in protocol_numbers_test.go. They cover each
type that decodes these values, all three transports, input requests,
and the compatibility parameter.
@Tangerg Tangerg changed the title mcp: preserve numbers in tool schemas and metadata mcp: preserve numbers in schemas and metadata Nov 6, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mcp: tool schemas and metadata lose JSON number precision

1 participant